package com.socialcoco.serviceImpl;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.socialcoco.entity.*;
import com.socialcoco.mapper.TgoodMapper;
import com.socialcoco.service.TgoodService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.socialcoco.util.timeGet;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 赵志浩
 * @since 2022-06-01 10:03:04
 */
@Service
@Slf4j
public class TgoodServiceImpl extends ServiceImpl<TgoodMapper, Tgood> implements TgoodService {


    private final String SECKILL_CHARE_PREFIX = "test:skus:";
    private final String SKU_STOCK_SEMAPHORE = "test:stock:";
    private final String SKU_USER_SEMAPHORE = "test:user:";
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    RedissonClient redissonClient;
    @Autowired
    TmygoodServiceImpl tmygoodService;
    @Override
    public List<Tgood> list() {
        if(redisTemplate.hasKey(SECKILL_CHARE_PREFIX)){
            Set<Object> keys = redisTemplate.opsForHash().keys(SECKILL_CHARE_PREFIX);
            //从redis中获取
            List<Object> list1=new ArrayList<>();
            for (Object key : keys) {
                list1.add(key);
            }
            List<Object> list = redisTemplate.opsForHash().multiGet(SECKILL_CHARE_PREFIX,list1);

            List<Tgood> collect = list.stream().map(item -> {
                String items = (String) item;
                Tgood redisTo = JSON.parseObject(items, Tgood.class);
                // redisTo.setRandomCode(null);当前秒杀开始需要随机码
                return redisTo;
            }).collect(Collectors.toList());
            return collect;
        }
        else {
            Long ttl = timeGet.getSecondsTobeforedawn();
            String token = UUID.randomUUID().toString().replace("-", "");
            List<Tgood> list = super.list();
            list.stream().forEach(item->{
                String seckillValue = JSON.toJSONString(item);
                redisTemplate.opsForHash().put(SECKILL_CHARE_PREFIX,String.valueOf(item.getTgid()),seckillValue);
                redisTemplate.expire(SECKILL_CHARE_PREFIX,ttl, TimeUnit.SECONDS);
                RSemaphore rSemaphore=redissonClient.getSemaphore(SKU_STOCK_SEMAPHORE+item.getTgid());
                rSemaphore.trySetPermits(item.getNum());
            });
            return list;
        }
    }

    /**
     * 点击秒杀兑换
     * 由于没有把用户的信息添加到thredlocal中，所以通过前端传过来
     * @return
     */
    @Override
//    @Transactional(rollbackFor =RuntimeException.class )
    @SentinelResource(value = "getSkusResource1",blockHandler = "blockHandler")
    public String killProduct(String uid,String key) throws InterruptedException, ExecutionException {
        long s2 = System.currentTimeMillis();
        //获取redis中的商品数据
        String o= (String) redisTemplate.opsForHash().get(SECKILL_CHARE_PREFIX, key);
        if(o==null){
            return "frame";
        }
        Tgood redisTo = JSON.parseObject(o, Tgood.class);
        //验证信号量是否大于0
        String s1 = redisTemplate.opsForValue().get(SKU_STOCK_SEMAPHORE + key);
        Integer count =Integer.valueOf(s1);
        if(count>0&&redisTo.getNum()>=count){
            //库存足够，限量正确
            String redisKey=uid+"-"+key;
            Long ttl = timeGet.getSecondsTobeforedawn();
            Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(SKU_USER_SEMAPHORE+redisKey, String.valueOf(1), ttl, TimeUnit.SECONDS);
            if(aBoolean){
                //为真说明今天没有兑换过,信号量减一
                RSemaphore semaphore=redissonClient.getSemaphore(SKU_STOCK_SEMAPHORE+key);
                boolean b = semaphore.tryAcquire(1, 100, TimeUnit.MILLISECONDS);
                if(b){
                    String o1= (String) redisTemplate.opsForValue().get(SKU_STOCK_SEMAPHORE+key);
                    int nums= Integer.valueOf(o1);
                    redisTo.setNum(nums);
                    String seckillValue = JSON.toJSONString(redisTo);
                    CompletableFuture<Void> future1=CompletableFuture.runAsync(()->{
                        Tmygood tmygood=new Tmygood();
                        tmygood.setMid(Integer.valueOf(uid));
                        tmygood.setTsid(Integer.valueOf(key));
                        tmygoodService.save(tmygood);
                    },threadPoolExecutor);
                    CompletableFuture<Void> future2=CompletableFuture.runAsync(()->{
                        redisTo.setNum(nums);
                        redisTemplate.opsForHash().put(SECKILL_CHARE_PREFIX,key,seckillValue);
                    },threadPoolExecutor);
                    CompletableFuture<Void> future3=CompletableFuture.runAsync(()->{
                        super.updateById(redisTo);
                    },threadPoolExecutor);
                    CompletableFuture.allOf(future1,future2,future3).get();



                    long s3 = System.currentTimeMillis();
                    log.info("耗时..." + (s2 - s3));
                }
            }
            else {
                return "exist";
            }

        }
        return null;
    }
    public static String blockHandler(String uid,String key,BlockException e) {

        log.error("getSkusResource被限流了,{}",e.getMessage());
        return null;
    }


}
